package Jihe;

import java.util.*;

/*
* Map接口：
* 1.键值对存储一组对象
* 2.Key保证唯一不重复，且一个Key对应一个value ， value可以重复
* 3.具体的实现类：HashMap ， TreeMap ， Hashtable ， LinkedHashMap
*
* HashMap 和 Hashtable的区别？
* 基于哈希表实现->(数组+链表)，默认数组大小为11，加载因子为0.75
* 可以进行初始化容量，
* 扩充方式：原数组大小<<1+1; (乘2加1)，
* 线程安全，用在多线程访问时
*
* 如何选择使用哪个呢？
* 如果存一个对象就用Collection,如果存两个对象就用Map
* 如果你要有排序，并且用二叉树存储---->TreeMap
* 不排序，单线程访问------>HashMap
* 不排序，多线程访问------>Hashtable
* 如果想在HashMap的基础上，保证存储的序号---->LinkedHashMap
*
* 数据结构：数组、链表、二叉树（红黑树），哈希表（数据+链表）、栈
* */
public class MapDemo {
    public static void main(String[] args) {
//        hashMap();
//        hashtable();
//        linkedHashMap();
        treemap();


    }

    /*
     * 基于二叉树的(红黑树)
     *
     * */
    private static void treemap(){
        Map<String,String> treemap = new TreeMap<>();
        treemap.put("one","Aily");
        treemap.put("two","mode");
        treemap.put("three","undo");
        treemap.put("four","bachelor");
        treemap.forEach((k,v)-> System.out.println(k+"->"+v));

        Map<Dog,String> dogs = new TreeMap<>();
        dogs.put(new Dog("戴瑞",2,1),"one");
        //此处菲齐的id(key)与戴瑞的id（key） 相同 ， 会替换掉戴瑞的值。
        dogs.put(new Dog("菲齐",1,1),"two");
        dogs.put(new Dog("凯迪",6,3),"three");
        dogs.forEach((k,v)-> System.out.println(k+"->"+v));
    }
    /*
    * LinkedHashMap是HashMap的子类，由于HashMap不能保证顺序恒久不变，
    * 此类使用一个双重链表来维护元素添加的顺序。
    * */
    private static void linkedHashMap(){
//        Map linked = new LinkedHashMap();
//        linked.put("one","oto");
//        linked.put("two","mot");
//        linked.put("three","onkg");
//        linked.put("four","qwhds");
//        linked.forEach((k,v)-> System.out.println(k+"->"+v));

    }
    /*
    * HashMap 和 Hashtable的区别？
    * 基于哈希表实现->(数组+链表)，默认数组大小为11，加载因子为0.75
    * 可以进行初始化容量，
    * 扩充方式：原数组大小<<1+1; (乘2加1)，
    * 线程安全，用在多线程访问时
    * */
    private static void hashtable(){
        Map<String,String> table = new Hashtable<>();
        table.put("one","Lily");
        table.put("two","tom");
        table.put("three","marie");
        table.put("four","bin");
        table.forEach((k,v)-> System.out.println(k+"->"+v));

    }
    /*
    * HashMap的实现原理：
    * 1.基于哈希表(数组+链表+二叉树(红黑树)1.8后)
    * 2.默认加载因子为0.75（存储数据到数组中的阈值在数组长度的75%） ，默认数组大小为16
    * 3、把对象存储到哈希表中？如何存储？   put方法
    * 把key对象通过 hash()方法计算哈希值，然后用哈希值对数组长度取余数，
    * 这个余数就是该key对象在数组中存储的下标位，当这个位置有多个对象时，以链表的结构存储，
    * 在jdk1.8后，当链表长度≥8时，将链表转换为二叉树（红黑树）存储。
    * 这样的目的是为了取值时提高效率，不需要遍历数组，存储的数据量越大，性能的表现越明显
    *扩充原理：当数组容量超过75%的时候（加载因子0.75），表示该数组需要扩充，扩充的算法？
    *扩充的算法是：当前数组容量<<1(相当于乘2)，扩大一倍
    * 扩大了以后，需要将数组中所有元素重新计算hashcode值，并且求出来的哈希值会对新数组长度
    * 取余，得到的余数就是该元素在新数组的下标位置。 这样的话，扩充次数过多，元素过多时，
    * 非常消耗性能。每次扩充表示哈希表重新散列（重新计算每个对象的存储位置），为了避免，我们
    * 在开发中尽量要减少扩充次数带来的性能问题。
    * 线程不安全，适合在单线程中使用。
    *  */
    private static void hashMap(){
        Map<Integer,String> map = new HashMap<>();
        map.put(1,"Aily");
        map.put(2,"tom");
        map.put(3,"jake");
        map.put(4,"marie");
        //System.out.println(map.size());
//        //从Map中取值 ， 已知键取值
//        System.out.println( map.get(1).equals("Aily"));
//        // 未知键取值 使用遍历1  遍历Entry
//        Set<Map.Entry<Integer,String>> entrySet = map.entrySet();
//        entrySet.forEach(System.out::println);
//        //遍历2  遍历键 ， 键拿值
//        Set<Integer> keySet = map.keySet();
//        for(Integer i:keySet){
//            String value = map.get(i);
//            System.out.println(i+"->"+value);
//        }
//        //遍历3  遍历值
//       Collection<String> values =  map.values();
//        for(String s:values){
//            System.out.println(s);
//        }
//          //遍历4 foreach
        map.forEach((key,value)-> System.out.println(key+"->"+value));
        System.out.println();

    }
}
